fix(trace): [4/4] 实际连接按链路聚合,消除新旧连接并存时的误报#1
Conversation
改规则后的过渡态下,同一域名会同时存在新连接(走新出口)和旧连接 (建连时定死的旧链路仍在复用)。旧版 [4/4] 逐条平铺所有活跃连接、却只取 其中一条与规则预测对比,于是出现"告警说实际走 DIRECT、底下列表又 proxy/ DIRECT 都有"的自相矛盾,看不懂。 根因有二: 1. 只挑一条做二元判定,多链路并存时必然误判; 2. "出口"一词两处指代不同层级——告警行取 chains[-1](入口组),详情行取 chains[0](末端节点)。 重写为: - 相同链路的连接合并计数、累加流量,不再逐条平铺; - 判定改用集合包含(预测组名是否出现在链路任意一跳),消除 chains 取头/取尾 的歧义; - 按命中情况分三种结论:全部一致 / 全部未命中(规则可能未生效)/ 新旧并存 (旧连接会随超时自然断开,刷新后即全部走预测出口); - 连接采集改为 host 精确匹配优先,仅在无 host(fake-ip)时回退按 IP 匹配, 避免 Cloudflare 等共享 IP 把同 IP 别站连接误算进来。 补 5 个单测覆盖三种结论 + host 优先 + IP 回退。
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca0dced77b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| elif di and di in resolved_ips: | ||
| ip_conns.append(c) |
There was a problem hiding this comment.
Restrict IP fallback to hostless connections
When no exact host match is found, this branch still collects every connection whose destinationIP is in resolved_ips, even if metadata.host is populated with a different domain. For CDN/shared-IP targets where the traced site has no active host-matched socket, [4/4] will still attribute another site's active connection on the same IP to the traced domain, so the Cloudflare-style false positive described by this change remains. The IP fallback should only include connections where the engine did not provide a host.
Useful? React with 👍 / 👎.
[4/4] 的 IP 回退分支原本只要 destinationIP 命中就收,host 非空但指向 别站的连接(同一 Cloudflare 共享 IP)在"目标站无 host 匹配连接"时仍会 被误算成本域名——上一版想消除的共享 IP 误报在这个边角没消干净。 IP 回退加 `not h` 限制:只回退真正拿不到 host 的连接(fake-ip 场景), host 非空的别站连接彻底排除。补回归测试覆盖该边角。 Codex review (PR#1) P2 — Restrict IP fallback to hostless connections.
|
@codex review — 已按建议把 IP 回退限定为 hostless 连接( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5bd00bc31b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| h = m.get("host", "") | ||
| di = m.get("destinationIP", "") | ||
| if (h == domain or h.endswith("." + domain) | ||
| or (di and di in resolved_ips)): | ||
| domain_conns.append(c) | ||
| if domain_conns: | ||
| if h == domain or h.endswith("." + domain): | ||
| host_conns.append(c) | ||
| elif not h and di and di in resolved_ips: |
There was a problem hiding this comment.
Use sniffHost before IP fallback
When TUN/sniffer traffic is present, mihomo can leave metadata.host empty while reporting the observed SNI/HTTP host in metadata.sniffHost; in that case this branch treats not h as true and falls back to destinationIP. For shared-IP destinations, trace aicodewith.com can still count an active other-site.com connection shaped like {host: "", sniffHost: "other-site.com", destinationIP: "104.26.4.164"}, so the shared-IP false positive survives unless sniffHost is folded into the host check before allowing IP fallback.
Useful? React with 👍 / 👎.
mihomo 在 sniffer/TUN 模式下可能把 metadata.host 留空、真实域名放在
sniffHost(嗅探到的 SNI/HTTP Host)。上一版用 `not h` 判断是否 IP 回退,
sniffHost 场景 host 为空仍会触发回退,别站连接
{host:"", sniffHost:"other-site.com"} 在共享 IP 下又被误算成本域名。
改为有效 host = host or sniffHost:host 匹配与 IP 回退都基于它,
只有 host 和 sniffHost 都空(纯 IP)才回退。顺带修了 sniffer 模式下
目标连接 host 为空、真实域名在 sniffHost 时被漏识别的问题。
补两个回归测试(sniffHost 命中纳入 / sniffHost 别站排除)。
Codex review (PR#1) P2#2 — Use sniffHost before IP fallback.
|
@codex review — 已把 sniffHost 折进有效 host(host or sniffHost),IP 回退仅在 host/sniffHost 都为空时触发,并补了回归测试 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dce94d260b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| h = m.get("host", "") | ||
| # sniffer/TUN 模式下 mihomo 可能把 host 留空、真实域名放在 | ||
| # sniffHost(嗅探到的 SNI / HTTP Host)——取二者作为有效 host | ||
| h = m.get("host", "") or m.get("sniffHost", "") |
There was a problem hiding this comment.
Match sniffHost even when host is populated
When mihomo reports both fields (for example host is the destination IP while sniffHost is the SNI/HTTP host), this or keeps only host, so a real sniffHost == domain connection is neither added to host_conns nor allowed to use the IP fallback because h is non-empty. In that sniffer/TUN case, proxyctl trace <domain> can still print “无活跃连接” or miss the route aggregation for the active target connection; consider checking host and sniffHost independently before deciding whether both are absent.
Useful? React with 👍 / 👎.
上一版用 `host or sniffHost` 合成有效 host,在 sniffer/TUN 把 host 填成
目的 IP、真实域名落在 sniffHost 时,host 非空(是 IP)会遮蔽 sniffHost:
{host:"104.26.4.164", sniffHost:"aicodewith.com"} 这条目标连接既不匹配
域名、又因 h 非空进不了 IP 回退 → 被漏掉,trace 误报"无活跃连接"。
改为两字段独立判断:任一匹配 domain 即纳入;只有 host 和 sniffHost 都
缺失(纯 IP)才按目的 IP 回退。彻底消除字段互相遮蔽。补回归测试。
Codex review (PR#1) P2#3 — Match sniffHost even when host is populated.
|
@codex review — 已改为 host 与 sniffHost 独立判断(任一匹配即纳入,两者皆空才回退 IP),修掉 host=IP 遮蔽 sniffHost 的漏报,补了回归测试 |
|
Codex Review: Didn't find any major issues. 🚀 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
动机
改路由规则(如
DOMAIN-SUFFIX,xxx,DIRECT→proxy)后的过渡态下,同一域名会同时存在新连接(走新出口)和旧连接(建连时定死的旧链路仍在 TLS 复用)。旧版proxyctl trace的[4/4] 实际连接逐条平铺所有活跃连接、却只取其中一条与规则预测对比,于是出现自相矛盾的输出:用户无法理解"预测/实际出口"是什么、为什么列表里两种都有。
根因
chains[-1](链路入口组名proxy),详情行取chains[0](末端节点名电信专用),同词不同义。改动
[4/4]改为按完整链路聚合:相同链路的连接合并计数、累加流量,不再逐条平铺。chains取头/取尾的歧义。destinationIP匹配,避免 Cloudflare 等共享 IP 把同 IP 其他站点的连接误算进当前域名。新输出:
测试
pytest805 passed。